/*******************************************************************************
 * Copyright (c) 2000, 2009 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     James Blackburn - Bug 256316 getImageDescriptor() is not thread safe 
 *******************************************************************************/
package org.eclipse.ui.internal.registry;

//import java.io.File;
import java.io.File;
import java.io.Serializable;

import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.graphics.Image;
import org.eclipse.ui.IEditorActionBarContributor;
import org.eclipse.ui.IEditorDescriptor;
import org.eclipse.ui.IEditorMatchingStrategy;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.IPluginContribution;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.internal.IWorkbenchConstants;
import org.eclipse.ui.internal.WorkbenchImages;
import org.eclipse.ui.internal.WorkbenchPlugin;
import org.eclipse.ui.internal.tweaklets.InterceptContributions;
import org.eclipse.ui.internal.tweaklets.Tweaklets;
import org.eclipse.ui.internal.util.Util;
import org.eclipse.ui.plugin.AbstractUIPlugin;

/**
 * @see IEditorDescriptor
 */
public final class EditorDescriptor implements IEditorDescriptor, Serializable,
        IPluginContribution {

    /**
     * Generated serial version UID for this class.
     */
    private static final long serialVersionUID = 3905241225668998961L;

    // @issue the following constants need not be public; see bug 47600
    /**
     * Open internal constant.  Value <code>0x01</code>.
     */
    public static final int OPEN_INTERNAL = 0x01;

    /**
     * Open in place constant.  Value <code>0x02</code>.
     */
    public static final int OPEN_INPLACE = 0x02;

    /**
     * Open external constant.  Value <code>0x04</code>.
     */
    public static final int OPEN_EXTERNAL = 0x04;

    private String editorName;

    private String imageFilename;

    private transient ImageDescriptor imageDesc;
    private transient Object imageDescLock = new Object();

    private boolean testImage = true;

    private String className;

    private String launcherName;

    private String fileName;

    private String id = Util.ZERO_LENGTH_STRING;

    private boolean matchingStrategyChecked = false;
    private IEditorMatchingStrategy matchingStrategy;

    // RAP [bm]: Program
//    private Program program;

    //The id of the plugin which contributed this editor, null for external editors
    private String pluginIdentifier;

    private int openMode = 0;

    private transient IConfigurationElement configurationElement;

	/**
     * Create a new instance of an editor descriptor. Limited
     * to internal framework calls.
     * @param element
     * @param id2
     */
    /* package */EditorDescriptor(String id2, IConfigurationElement element) {
        setID(id2);
        setConfigurationElement(element);
    }

    

	/**
	 * Create a new instance of an editor descriptor. Limited
     * to internal framework calls.
	 */
    /* package */ EditorDescriptor() {
		super();
	}


    
// RAP [rh] (external) program editors not supported
//	/**
//     * Creates a descriptor for an external program.
//     * 
//     * @param filename the external editor full path and filename
//     * @return the editor descriptor
//     */
//    public static EditorDescriptor createForProgram(String filename) {
//        if (filename == null) {
//            throw new IllegalArgumentException();
//        }
//        EditorDescriptor editor = new EditorDescriptor();
//
//        editor.setFileName(filename);
//        editor.setID(filename);
//        editor.setOpenMode(OPEN_EXTERNAL);
//
//        //Isolate the program name (no directory or extension)
//        int start = filename.lastIndexOf(File.separator);
//        String name;
//        if (start != -1) {
//            name = filename.substring(start + 1);
//        } else {
//            name = filename;
//        }
//        int end = name.lastIndexOf('.');
//        if (end != -1) {
//            name = name.substring(0, end);
//        }
//        editor.setName(name);
//
//        // get the program icon without storing it in the registry
//        ImageDescriptor imageDescriptor = new ProgramImageDescriptor(filename,
//                0);
//        editor.setImageDescriptor(imageDescriptor);
//
//        return editor;
//    }
    
    // RAP [bm]: 
//    /**
//     * Return the program called programName. Return null if it is not found.
//     * @return org.eclipse.swt.program.Program
//     */
//    private static Program findProgram(String programName) {
//
//        Program[] programs = Program.getPrograms();
//        for (int i = 0; i < programs.length; i++) {
//            if (programs[i].getName().equals(programName)) {
//				return programs[i];
//			}
//        }
//
//        return null;
//    }

    /**
     * Create the editor action bar contributor for editors of this type.
     * 
     * @return the action bar contributor, or <code>null</code>
     */
    public IEditorActionBarContributor createActionBarContributor() {
        // Handle case for predefined editor descriptors, like the
        // one for IEditorRegistry.SYSTEM_INPLACE_EDITOR_ID, which
        // don't have a configuration element.
        if (configurationElement == null) {
            return null;
        }

        // Get the contributor class name.
        String className = configurationElement
                .getAttribute(IWorkbenchRegistryConstants.ATT_CONTRIBUTOR_CLASS);
        if (className == null) {
			return null;
		}

        // Create the contributor object.
        IEditorActionBarContributor contributor = null;
        try {
            contributor = (IEditorActionBarContributor) WorkbenchPlugin
                    .createExtension(configurationElement,
                            IWorkbenchRegistryConstants.ATT_CONTRIBUTOR_CLASS);
        } catch (CoreException e) {
            WorkbenchPlugin.log("Unable to create editor contributor: " + //$NON-NLS-1$
                    id, e.getStatus());
        }
        return contributor;
    }

    /**
     * Return the editor class name.
     * 
     * @return the class name
     */
    public String getClassName() {
    	if (configurationElement == null) {
    		return className;
    	}
    	return RegistryReader.getClassValue(configurationElement,
                IWorkbenchRegistryConstants.ATT_CLASS);
    }

    /**
     * Return the configuration element used to define this editor, or <code>null</code>.
     * 
     * @return the element or null
     */
    public IConfigurationElement getConfigurationElement() {
        return configurationElement;
    }
    
    /**
     * Create an editor part based on this descriptor.
     * 
     * @return the editor part
     * @throws CoreException thrown if there is an issue creating the editor
     */
    public IEditorPart createEditor() throws CoreException {        
        Object extension = WorkbenchPlugin.createExtension(getConfigurationElement(), IWorkbenchRegistryConstants.ATT_CLASS);
        return ((InterceptContributions)Tweaklets.get(InterceptContributions.KEY)).tweakEditor(extension);
    }

    /**
     * Return the file name of the command to execute for this editor.
     * 
     * @return the file name to execute
     */
    public String getFileName() {
    	// RAP [bm]: program 
//        if (program == null) {
        	if (configurationElement == null) {
        		return fileName;
        	}
        	return configurationElement.getAttribute(IWorkbenchRegistryConstants.ATT_COMMAND);
//    	}
//        return program.getName();
    }

    /**
     * Return the id for this editor.
     * 
     * @return the id
     */
    public String getId() {        
    	// RAP [bm]: Program
//        if (program == null) {
        	if (configurationElement == null) {
        		return Util.safeString(id);
        	}
        	return Util.safeString(configurationElement.getAttribute(IWorkbenchRegistryConstants.ATT_ID));
        	
//        }
//        return Util.safeString(program.getName());
    }

    /**
     * Return the image descriptor describing this editor.
     * 
     * @return the image descriptor
     */
    public ImageDescriptor getImageDescriptor() {
    	ImageDescriptor tempDescriptor = null;

    	synchronized (imageDescLock) {
	    	if (!testImage)
	            return imageDesc;
	    	
			if (imageDesc == null) {
				String imageFileName = getImageFilename();
// RAP [rh] unused code					
//				String command = getFileName();
				if (imageFileName != null && configurationElement != null)
					tempDescriptor = AbstractUIPlugin.imageDescriptorFromPlugin(configurationElement.getNamespaceIdentifier(), imageFileName);
// RAP [rh] program image descriptors not supported					
//				} else if (command != null) {
//					imageDesc = WorkbenchImages.getImageDescriptorFromProgram(
//							command, 0);
				}
    		
			if (tempDescriptor == null) { // still null? return default image
				imageDesc = WorkbenchImages.getImageDescriptor(ISharedImages.IMG_OBJ_FILE);
				testImage = false;
		        return imageDesc;
			}
    	}
    	
		// Verifies that the image descriptor generates an image.  If not, the descriptor is 
    	// replaced with the default image.
    	// We must create the image without holding any locks, since there is a potential for deadlock
    	// on Linux due to SWT's implementation of Image. See bugs 265028 and 256316 for details.
		Image img = tempDescriptor.createImage(false);
		if (img == null) // @issue what should be the default image?
			tempDescriptor = WorkbenchImages.getImageDescriptor(ISharedImages.IMG_OBJ_FILE);
		else
		    img.dispose();
		// <----- End of must-not-lock part
		
		// reenter synchronized block
		synchronized (imageDescLock) {
			// if another thread has set the image description, use it
			if (!testImage)
				return imageDesc;
			// otherwise set the image description we calculated above
			imageDesc = tempDescriptor;
			testImage = false;
			return imageDesc;
		}
    }

    /**
     * The Image to use to repesent this editor
     */
    /* package */void setImageDescriptor(ImageDescriptor desc) {
    	synchronized (imageDescLock) {
	        imageDesc = desc;
	        testImage = true;
    	}
    }

    /**
     * The name of the image describing this editor.
     * 
     * @return the image file name
     */
    public String getImageFilename() {
    	if (configurationElement == null) {
			return imageFilename;
		}
    	return configurationElement.getAttribute(IWorkbenchRegistryConstants.ATT_ICON);
    }

    /**
     * Return the user printable label for this editor.
     * 
     * @return the label
     */
    public String getLabel() {
    	// RAP [bm]: Program
//        if (program == null) {
        	if (configurationElement == null) {
        		return editorName;        		
        	}
        	return configurationElement.getAttribute(IWorkbenchRegistryConstants.ATT_NAME);
//        }
//        return program.getName();
    }

    /**
     * Returns the class name of the launcher.
     * 
     * @return the launcher class name
     */
    public String getLauncher() {
    	if (configurationElement == null) {
			return launcherName;
		}
    	return configurationElement.getAttribute(IWorkbenchRegistryConstants.ATT_LAUNCHER);
    }

    /**
     * Return the contributing plugin id.
     * 
     * @return the contributing plugin id
     */
    public String getPluginID() {
    	if (configurationElement != null) {
			return configurationElement.getNamespace();
		}
    	return pluginIdentifier;
    }

    // RAP [bm]: 
//    /**
//     * Get the program for the receiver if there is one.
//     * @return Program
//     */
//    public Program getProgram() {
//        return this.program;
//    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.IEditorDescriptor#isInternal
     */
    public boolean isInternal() {
        return getOpenMode() == OPEN_INTERNAL;
    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.IEditorDescriptor#isOpenInPlace
     */
    public boolean isOpenInPlace() {
        return getOpenMode() == OPEN_INPLACE;
    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.IEditorDescriptor#isOpenExternal
     */
    public boolean isOpenExternal() {
        return getOpenMode() == OPEN_EXTERNAL;
    }

    /**
     * Load the object properties from a memento.
     * 
     * @return <code>true</code> if the values are valid, <code>false</code> otherwise
     */
    protected boolean loadValues(IMemento memento) {
        editorName = memento.getString(IWorkbenchConstants.TAG_LABEL);
        imageFilename = memento.getString(IWorkbenchConstants.TAG_IMAGE);
        className = memento.getString(IWorkbenchConstants.TAG_CLASS);
        launcherName = memento.getString(IWorkbenchConstants.TAG_LAUNCHER);
        fileName = memento.getString(IWorkbenchConstants.TAG_FILE);
        id = Util.safeString(memento.getString(IWorkbenchConstants.TAG_ID));
        pluginIdentifier = memento.getString(IWorkbenchConstants.TAG_PLUGIN);

        Integer openModeInt = memento
                .getInteger(IWorkbenchConstants.TAG_OPEN_MODE);
        if (openModeInt != null) {
            openMode = openModeInt.intValue();
        } else {
            // legacy: handle the older attribute names, needed to allow reading of pre-3.0-RCP workspaces 
            boolean internal = new Boolean(memento
                    .getString(IWorkbenchConstants.TAG_INTERNAL))
                    .booleanValue();
            boolean openInPlace = new Boolean(memento
                    .getString(IWorkbenchConstants.TAG_OPEN_IN_PLACE))
                    .booleanValue();
            if (internal) {
                openMode = OPEN_INTERNAL;
            } else {
                if (openInPlace) {
                    openMode = OPEN_INPLACE;
                } else {
                    openMode = OPEN_EXTERNAL;
                }
            }
        }
        if (openMode != OPEN_EXTERNAL && openMode != OPEN_INTERNAL
                && openMode != OPEN_INPLACE) {
            WorkbenchPlugin
                    .log("Ignoring editor descriptor with invalid openMode: " + this); //$NON-NLS-1$
            return false;
        }

        // RAP [bm]: 
//        String programName = memento
//                .getString(IWorkbenchConstants.TAG_PROGRAM_NAME);
//        if (programName != null) {
//            this.program = findProgram(programName);
//        }
        // RAPEND: [bm] 

        return true;
    }

    /**
     * Save the object values in a IMemento
     */
    protected void saveValues(IMemento memento) {
        memento.putString(IWorkbenchConstants.TAG_LABEL, getLabel());
        memento.putString(IWorkbenchConstants.TAG_IMAGE, getImageFilename());
        memento.putString(IWorkbenchConstants.TAG_CLASS, getClassName());
        memento.putString(IWorkbenchConstants.TAG_LAUNCHER, getLauncher());
        memento.putString(IWorkbenchConstants.TAG_FILE, getFileName());
        memento.putString(IWorkbenchConstants.TAG_ID, getId());
        memento.putString(IWorkbenchConstants.TAG_PLUGIN, getPluginId());

        memento.putInteger(IWorkbenchConstants.TAG_OPEN_MODE, getOpenMode());
        // legacy: handle the older attribute names, needed to allow reading of workspace by pre-3.0-RCP eclipses
        memento.putString(IWorkbenchConstants.TAG_INTERNAL, String
                .valueOf(isInternal()));
        memento.putString(IWorkbenchConstants.TAG_OPEN_IN_PLACE, String
                .valueOf(isOpenInPlace()));

        // RAP [bm]: Program
//        if (this.program != null) {
//			memento.putString(IWorkbenchConstants.TAG_PROGRAM_NAME,
//                    this.program.getName());
//		}
    }

    /**
     * Return the open mode of this editor.
     *
	 * @return the open mode of this editor
	 */
	private int getOpenMode() {
		if (configurationElement == null) { // if we've been serialized, return our serialized value
			return openMode;
		}
		else if (getLauncher() != null) {
            // open using a launcer
        	return EditorDescriptor.OPEN_EXTERNAL;
        } else if (getFileName() != null) {
            // open using an external editor 	
            return EditorDescriptor.OPEN_EXTERNAL;
        } else if (getPluginId() != null) {
        	// open using an internal editor
        	return EditorDescriptor.OPEN_INTERNAL;
        }
        else {
        	return 0; // default for system editor
        }
	}

	/**
     * Set the class name of an internal editor.
     */
    /* package */void setClassName(String newClassName) {
        className = newClassName;
    }

    /**
     * Set the configuration element which contributed this editor.
     */
    /* package */void setConfigurationElement(
            IConfigurationElement newConfigurationElement) {
        configurationElement = newConfigurationElement;
    }

    /**
     * Set the filename of an external editor.
     */
    /* package */void setFileName(String aFileName) {
        fileName = aFileName;
    }

    /**
     * Set the id of the editor.
     * For internal editors this is the id as provided in the extension point
     * For external editors it is path and filename of the editor
     */
    /* package */void setID(String anID) {
        Assert.isNotNull(anID);
        id = anID;
    }

    /**
     * The name of the image to use for this editor.
     */
    /* package */void setImageFilename(String aFileName) {
        imageFilename = aFileName;
    }

    /**
     * Sets the new launcher class name
     *
     * @param newLauncher the new launcher
     */
    /* package */void setLauncher(String newLauncher) {
        launcherName = newLauncher;
    }

    /**
     * The label to show for this editor.
     */
    /* package */void setName(String newName) {
        editorName = newName;
    }

    /**
     * Sets the open mode of this editor descriptor.
     * 
     * @param mode the open mode
     * 
     * @issue this method is public as a temporary fix for bug 47600
     */
    public void setOpenMode(int mode) {
        openMode = mode;
    }

    /**
     * The id of the plugin which contributed this editor, null for external editors.
     */
    /* package */void setPluginIdentifier(String anID) {
        pluginIdentifier = anID;
    }

    // RAP [bm]: 
//    /**
//     * Set the receivers program.
//     * @param newProgram
//     */
//    /* package */void setProgram(Program newProgram) {
//
//        this.program = newProgram;
//        if (editorName == null) {
//			setName(newProgram.getName());
//		}
//    }

    /**
     * For debugging purposes only.
     */
    public String toString() {
        return "EditorDescriptor(id=" + getId() + ", label=" + getLabel() + ")"; //$NON-NLS-2$ //$NON-NLS-3$//$NON-NLS-1$
    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.activities.support.IPluginContribution#getLocalId()
     */
    public String getLocalId() {
        return getId();
    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.activities.support.IPluginContribution#getPluginId()
     */
    public String getPluginId() {
        return getPluginID();
    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.IEditorDescriptor#getEditorManagementPolicy()
     */
    public IEditorMatchingStrategy getEditorMatchingStrategy() {
        if (matchingStrategy == null && !matchingStrategyChecked) {
            matchingStrategyChecked = true;
            // RAP [bm]: Program
//            if (program == null && configurationElement != null) {
            if (configurationElement != null) {
            // RAPEND: [bm] 
                if (configurationElement.getAttribute(IWorkbenchRegistryConstants.ATT_MATCHING_STRATEGY) != null) {
                    try {
                        matchingStrategy = (IEditorMatchingStrategy) WorkbenchPlugin.createExtension(configurationElement, IWorkbenchRegistryConstants.ATT_MATCHING_STRATEGY);
                    } catch (CoreException e) {
                        WorkbenchPlugin.log("Error creating editor management policy for editor id " + getId(), e); //$NON-NLS-1$
                    }
                }
            }
        }
        return matchingStrategy;
    }
    
    
}
